# -*- encoding: utf-8 -*-

"""
------------------------------------------
@File       : 37.custom_datatype.py
@Author     : maixiaochai
@Email      : maixiaochai@outlook.com
@CreatedOn  : 2021/8/16 11:01
------------------------------------------
    37. 用组合起来的类来实现多层结构，不要用嵌套的内置类型

    代码实现：用成绩册记录学生的分数，根据各科权重，求出平均成绩
"""
from collections import namedtuple, defaultdict

# 成绩
Grade = namedtuple('Grade', ('score', 'weight'))


class Subject:
    """
        学科
    """

    def __init__(self):
        self._grades = []

    def report_grade(self, score, weight):
        self._grades.append(Grade(score, weight))

    def average_grade(self):
        total, total_weight = 0, 0

        for grade in self._grades:
            total += grade.score * grade.weight
            total_weight += grade.weight

        return total / total_weight


class Student:
    """
        学生类
    """

    def __init__(self):
        # defaultdict, 特点是：在所给的key不存在时，不会报错，而是返回给定类型的空值
        self._subjects = defaultdict(Subject)

    def get_subject(self, name):
        return self._subjects[name]

    def average_grade(self):
        total, count = 0, 0
        for subject in self._subjects.values():
            total += subject.average_grade()
            count += 1

        return total / count


class GradeBook:
    """
        成绩册
    """

    def __init__(self):
        self._students = defaultdict(Student)

    def get_student(self, name):
        """
            如果成绩册里还没有记录过这位学生，那么在调用 get_student方法时，
            GradeBook就会构造一个默认的 Student对象给调用者
        """
        return self._students[name]


def main():
    book = GradeBook()
    albert = book.get_student('Albert Einstein')

    math = albert.get_subject('Math')
    math.report_grade(75, 0.05)
    math.report_grade(65, 0.15)
    math.report_grade(70, 0.80)

    gym = albert.get_subject('Gym')
    gym.report_grade(100, 0.40)
    gym.report_grade(85, 0.60)

    print(albert.average_grade())


if __name__ == '__main__':
    main()
